home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / rclesrc.10 / TCP Libraries / TCPStuff.unit < prev    next >
Encoding:
Text File  |  1992-06-27  |  21.8 KB  |  765 lines

  1. unit TCPStuff;
  2.  
  3. { This program was written by Peter N Lewis, Mar 1992 in THINK Pascal 4.0.1 }
  4.  
  5. interface
  6.  
  7. uses
  8.     TCPTypes;
  9.  
  10. const
  11.     Minimum_TCPBUFFERSIZE = 4096;
  12.     Default_TCPBUFFERSIZE = longInt(6) * 1024;
  13.     { Amount of space to allocate for each TCP connection }
  14.     INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  15.     control_block_max = 260;
  16.     tooManyControlBlocks = -23098;
  17.  
  18. type
  19.     OSErrPtr = ^OSErr;
  20.  
  21. { TCP connection description: }
  22.     TCPConnectionType = record
  23.             magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  24.             stream: StreamPtr;
  25.             asends, asendcompletes: longInt;
  26.             closedone: boolean;
  27.             closeuserptr: OSErrPtr;
  28.             incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  29.             incomingSize: longInt;                        { Number of bytes left in inBuf. }
  30.             buffer: ptr;        { connection buffer. }
  31.             inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  32.         end;
  33.     TCPConnectionPtr = ^TCPConnectionType;
  34.  
  35.     MyControlBlock = record
  36.             tcp: TCPControlBlock;
  37.             inuse: boolean;
  38.             userptr: OSErrPtr;
  39.             proc: procPtr;
  40.             tcpc: TCPConnectionPtr;
  41.         end;
  42.     MyControlBlockPtr = ^MyControlBlock;
  43.  
  44.  
  45.     TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  46.         T_Closing, T_PleaseClose, T_Unknown);
  47.  
  48. function TCPNameToAddr (var hostName: str255; timeout: longInt; var hostFile: str255): longInt;
  49. function TCPOpenResolver (var hostFile: str255; var dataptr: ptr): OSErr;
  50. function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  51. procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  52. function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  53. procedure TCPCloseResolver (dataptr: ptr);
  54.  
  55. function C2PStr (s: stringPtr): stringPtr;
  56. procedure SanitizeHostName (var s: str255);
  57.  
  58. function TCPInit: OSErr;
  59. procedure TCPFinish;
  60. function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  61. function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  62. function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  63. function TCPFlush (connection: TCPConnectionptr): OSErr;
  64. function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  65. function TCPAbort (connection: TCPConnectionPtr): OSErr;
  66. function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  67. procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  68. function TCPState (connection: TCPConnectionPtr): TCPStateType;
  69. function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  70. function TCPLocalPort (connection: TCPConnectionPtr): integer;
  71. function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  72. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  73. function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  74. function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  75. function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  76.                             charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  77.                             var gottermchar: boolean): OSErr;
  78. function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  79. function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  80.  
  81. implementation
  82.  
  83. {    Loosely based on code by Harry Chesley 12/88, thus Copyright ⌐ 1988 Apple Computer, Inc.}
  84. {    Converted to sensible pascal interface 7/91 by Peter Lewis, thus also Copyright ⌐ 1991 Peter Lewis }
  85.  
  86. const
  87.     MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  88.     dispose_block_max = 100;
  89.  
  90. type
  91.     MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  92.  
  93. var
  94.     driver_refnum: integer;
  95.     controlblocks: MyControlBlockArray;
  96.     max_dispose_block: integer;
  97.     disposeblocks: array[1..dispose_block_max] of ptr;
  98.  
  99. function C2PStr (s: StringPtr): StringPtr;
  100.     var
  101.         n, i: integer;
  102.     begin
  103.         n := 0;
  104.         while s^[n] <> chr(0) do
  105.             n := succ(n);
  106.         for i := n downto 1 do
  107.             s^[i - 1] := s^[i];
  108.         s^[0] := chr(n);
  109.         C2PStr := s
  110.     end;
  111.  
  112. procedure SanitizeHostName (var s: str255);
  113.     var
  114.         dummysp: stringPtr;
  115.     begin
  116.         dummysp := C2PStr(@s);
  117. {$PUSH}
  118. {$R-}
  119.         if s[Length(s)] = '.' then
  120.             s[0] := chr(Length(s) - 1);
  121. {$POP}
  122.     end;
  123.  
  124. function GetA6: ptr;
  125. inline
  126.     $2F4E, $0000;
  127.  
  128. procedure CallCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  129. inline
  130.     $205F, $4E90;
  131.  
  132. {$PUSH}
  133. {$D-}
  134. procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  135.     type
  136.         stackframe = packed record
  137.                 frameptr: ptr;
  138.                 returnptr: ptr;
  139.                 paramblockptr: MyControlBlockPtr;
  140.             end;
  141.         stackframeptr = ^stackframe;
  142.     var
  143.         a6: stackframeptr;
  144.         cbp: MyControlBlockPtr;
  145.     begin
  146.         a6 := stackframeptr(GetA6);
  147.         cbp := a6^.paramblockptr;
  148.         with cbp^ do begin
  149.             if userptr <> nil then
  150.                 userptr^ := cbp^.tcp.ioResult;
  151.             inuse := false;
  152.             if proc <> nil then
  153.                 CallCompletion(cbp, proc);
  154.         end;
  155.     end;
  156.  
  157. procedure ZotBlocks;
  158.     begin
  159.         while max_dispose_block > 0 do begin
  160.             DisposPtr(disposeblocks[max_dispose_block]);
  161.             max_dispose_block := max_dispose_block - 1;
  162.         end;
  163.     end;
  164.  
  165. procedure AddBlock (p: univ ptr);
  166.     begin
  167.         if max_dispose_block < dispose_block_max then begin
  168.             max_dispose_block := max_dispose_block + 1;
  169.             disposeblocks[max_dispose_block] := p;
  170.         end;
  171.     end;
  172.  
  173. procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  174.     { Zero out the control block parameters. }
  175.     var
  176.         i: integer;
  177.         p: longInt;
  178.     begin
  179.         ZotBlocks;
  180.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  181.             ptr(p)^ := 0;
  182.         cb.tcpStream := stream;
  183.         cb.ioCRefNum := driver_refnum;
  184.         cb.csCode := call;
  185.     end;
  186.  
  187. function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  188. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  189.     var
  190.         i: integer;
  191.     begin
  192.         i := 1;
  193.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  194.             i := i + 1;
  195.         cbp := controlblocks[i];
  196.         if cbp = nil then begin
  197.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  198.             if cbp <> nil then begin
  199.                 cbp^.inuse := false;
  200.                 controlblocks[i] := cbp;
  201.             end;
  202.         end;
  203.         if (cbp <> nil) & not cbp^.inuse then begin
  204.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  205.             cbp^.tcp.ioCompletion := @IOCompletion;
  206.             cbp^.inuse := true;
  207.             cbp^.userptr := userptr;
  208.             cbp^.tcpc := tcpc;
  209.             cbp^.proc := proc;
  210.             if userptr <> nil then
  211.                 userptr^ := inprogress;
  212.             GetCB := noErr;
  213.         end
  214.         else begin
  215.             cbp := nil;
  216.             GetCB := memFullErr;
  217.         end;
  218.     end;
  219.  
  220. procedure FreeCB (var cbp: MyControlBlockPtr);
  221.     begin
  222.         if cbp <> nil then
  223.             cbp^.inuse := false;
  224.         cbp := nil;
  225.     end;
  226. {$POP}
  227.  
  228. {$S Init}
  229. function TCPInit: OSErr;
  230.     var
  231.         oe: OSErr;
  232.         i: integer;
  233.     begin
  234.         max_dispose_block := 0;
  235.         oe := OpenDriver('.IPP', driver_refnum);
  236.         for i := 1 to control_block_max do
  237.             controlblocks[i] := nil;
  238.         TCPInit := oe;
  239.     end;
  240.  
  241. {$S Term}
  242. procedure TCPFinish;
  243.     var
  244.         i: integer;
  245.     begin
  246.         for i := 1 to control_block_max do
  247.             if controlblocks[i] <> nil then begin
  248.                 DisposPtr(ptr(controlblocks[i]));
  249.                 controlblocks[i] := nil;
  250.             end;
  251.     end;
  252.  
  253. {$S}
  254. procedure DestroyConnection (var connection: TCPConnectionPtr);
  255.     begin
  256.         connection^.magic := '????';
  257.         if connection^.buffer <> nil then
  258.             DisposPtr(ptr(connection^.buffer));
  259.         DisposPtr(Ptr(connection));
  260.         connection := nil;
  261.     end;
  262.  
  263. function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  264.     begin
  265.         if connection = nil then
  266.             ValidateConnection := connectionDoesntExist
  267.         else if connection^.magic <> MAGICNUMBER then
  268.             ValidateConnection := connectionDoesntExist
  269.         else
  270.             ValidateConnection := noErr;
  271.     end;
  272.  
  273. function PBControlSync (var cb: TCPControlBlock): OSErr;
  274.     begin
  275.         PBControlSync := PBControl(@cb, false);
  276.     end;
  277.  
  278. {$PUSH}
  279. {$D-}
  280. function PBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  281.     var
  282.         oe: OSErr;
  283.     begin
  284.         oe := PBControl(ParmBlkPtr(cbp), true);
  285.         if oe <> noErr then
  286.             FreeCB(cbp);
  287.         PBControlAsync := oe;
  288.     end;
  289. {$POP}
  290.  
  291. function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  292.     var
  293.         cb: TCPControlBlock;
  294.         oe: OSErr;
  295.     begin
  296.         ZeroCB(cb, nil, TCPcsGetMyIP);
  297.         oe := PBControlSync(cb);
  298.         myIP := cb.getmyip.ourAddress;
  299.         TCPGetMyIPAddr := oe;
  300.     end;
  301.  
  302. procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  303.     begin
  304.         if userptr <> nil then begin
  305.             if oe <> noErr then
  306.                 userptr^ := oe;
  307.         end;
  308.     end;
  309.  
  310. function CreateStream (var connection: TCPConnectionPtr; buffersize: longInt): OSErr;
  311.     var
  312.         oe: OSErr;
  313.         cb: TCPControlBlock;
  314.     begin
  315.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  316.         if connection = nil then
  317.             oe := memFullErr
  318.         else
  319.             with connection^ do begin
  320.                 buffer := NewPtr(buffersize);
  321.                 if buffer = nil then begin
  322.                     oe := memFullErr;
  323.                     DisposPtr(ptr(connection));
  324.                     connection := nil;
  325.                 end
  326.                 else begin
  327.                     magic := MAGICNUMBER;
  328.                     asends := 0;
  329.                     asendcompletes := 0;
  330.                     closedone := false;
  331.                     incomingSize := 0;
  332.                     ZeroCB(cb, nil, TCPcsCreate);
  333.                     cb.create.rcvBuff := buffer;
  334.                     cb.create.rcvBuffLen := buffersize;
  335.                     oe := PBControlSync(cb);
  336.                     stream := cb.tcpStream;
  337.                 end;
  338.             end;
  339.         if (oe <> noErr) and (connection <> nil) then
  340.             DestroyConnection(connection);
  341.         CreateStream := oe;
  342.     end;
  343.  
  344. function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  345.     var
  346.         oe, ooe: OSErr;
  347.         cbp: MyControlBlockPtr;
  348.         cb: TCPControlBlock;
  349.     begin
  350.         oe := CreateStream(connection, buffersize);
  351.         if oe = noErr then begin
  352.             with connection^ do begin
  353.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  354.                 if oe = noErr then begin
  355.                     cbp^.tcp.open.localPort := localPort;
  356.                     cbp^.tcp.open.remoteHost := remoteIP;
  357.                     cbp^.tcp.open.remotePort := remoteport;
  358.                     oe := PBControlAsync(cbp);
  359.                 end;
  360.                 if oe <> noErr then begin
  361.                     ZeroCB(cb, stream, TCPcsRelease);
  362.                     ooe := PBControlSync(cb);
  363.                     DestroyConnection(connection);
  364.                 end;
  365.             end;
  366.         end;
  367.         SetUserPtr(userptr, oe);
  368.         PAOpen := oe;
  369.     end;
  370.  
  371. { Open a connection to another machine }
  372. function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  373.     begin
  374.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  375.     end;
  376.  
  377. { Open a socket on this machine, to wait for a connection }
  378. function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  379.     begin
  380.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  381.     end;
  382.  
  383. function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  384. { Return readCount characters from the TCP connection. }
  385. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  386.     var
  387.         cb: TCPControlBlock;
  388.         oe: OSErr;
  389.     begin
  390.         repeat
  391.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  392.             cb.receive.rcvBuff := returnPtr;
  393.             cb.receive.rcvBuffLength := readCount;
  394.             oe := PBControlSync(cb);
  395.             longInt(returnPtr) := longInt(returnPtr) + cb.receive.rcvBuffLength;
  396.             readCount := readCount - cb.receive.rcvBuffLength;
  397.         until (oe <> noErr) or (readCount = 0);
  398.         TCPRawReceiveChars := oe;
  399.     end;
  400.  
  401. { Return readCount characters from the TCP connection.}
  402. function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  403.     var
  404.         readCountStr: Str255;
  405.         l: longInt;
  406.         p: Ptr;
  407.         oe: OSErr;
  408.         cb: TCPControlBlock;
  409.     begin
  410.         oe := ValidateConnection(connection);
  411.         if oe = noErr then
  412.             if readCount < 0 then
  413.                 oe := invalidLength
  414.             else if readCount > 0 then begin
  415.                 p := returnPtr;
  416.                 with connection^ do
  417.                     if incomingSize > 0 then begin
  418.             { Read as much as there is or as much as we need, whichever is less. }
  419.                         if readCount < incomingSize then
  420.                             l := readCount
  421.                         else
  422.                             l := incomingSize;
  423.                         BlockMove(incomingPtr, p, l);
  424.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  425.                         incomingSize := incomingSize - l;
  426.                         p := Ptr(ord4(p) + l);
  427.                         readCount := readCount - l;
  428.                     end;
  429.                 { If there's more needed, then read it from the connection. }
  430.                 if readCount > 0 then begin
  431.                         { Issue a read and wait until it all arrives). }
  432.                     oe := TCPRawReceiveChars(connection, p, readCount);
  433.                 end;
  434.             end;
  435.         TCPReceiveChars := oe;
  436.     end;
  437.  
  438. function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  439.         { Return the next byte in the buffer, reading more in if necessary. }
  440.     var
  441.         waitUntil: longInt;
  442.         readIn: longInt;
  443.         oe: OSErr;
  444.         cb: TCPControlBlock;
  445.     begin
  446.         oe := ValidateConnection(connection);
  447.         if oe = noErr then
  448.             with connection^ do begin            { Check if we need to read in more bytes. }
  449.                 if incomingSize = 0 then begin
  450.                     if timeout = 0 then
  451.                         oe := commandTimeout
  452.                     else begin
  453.                         waitUntil := TickCount + timeout;
  454.     { keep on trying to read until we get at least one, or the time-out happens. }
  455.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  456.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  457.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  458.                                 if readIn > INCOMINGBUFSIZE then
  459.                                     readIn := INCOMINGBUFSIZE;
  460.                         { Issue the read. }
  461.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  462.                                 if oe = noErr then begin
  463.                                     incomingSize := readIn;
  464.                                     incomingPtr := @inBuf;
  465.                                 end;
  466.                             end        { If not, do another round or get out, depending on the timeout condition. }
  467.                             else if TickCount > waitUntil then begin
  468.                                 oe := commandTimeOut;
  469.                             end;
  470.                         end;
  471.                     end;
  472.                 end;
  473.                 { Get the byte to return. }
  474.                 if incomingSize > 0 then begin
  475.                     b := incomingPtr^;
  476.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  477.                     incomingSize := incomingSize - 1;
  478.                 end
  479.                 else
  480.                     b := 0;
  481.             end;
  482.         TCPReadByte := oe;
  483.     end;
  484.  
  485. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  486. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  487. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  488. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  489. { will read the entire buffer, and any characters that arrive before a timeout }
  490. function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  491.                                 charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  492.                                 var gottermchar: boolean): OSErr;
  493.     var
  494.         oe: OSErr;
  495.  
  496.     procedure putByte (b: signedByte);
  497.         { Put the byte b after the output handle, increasing the handle's size in the process. }
  498.         var
  499.             p: Ptr;
  500.         begin
  501.             p := Ptr(ord4(readPtr) + readPos);
  502.             p^ := b;
  503.             readPos := readPos + 1;
  504.         end;
  505.  
  506.         var
  507.             inChar: SignedByte;
  508.  
  509.     begin
  510.         oe := ValidateConnection(connection);
  511.         gottermchar := false;
  512. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  513.         while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  514.             oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  515.             if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  516.                 putByte(inChar);                    { Check for the end. }
  517.                 gottermchar := inChar = termChar;
  518.             end;
  519.         end;
  520.         if oe = commandTimeOut then
  521.             oe := noErr;
  522.         TCPReceiveUpTo := oe;
  523.     end;
  524.  
  525. function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  526.     var
  527.         wds: wdsType;
  528.         oe: OSErr;
  529.         cb: TCPControlBlock;
  530.         p: ptr;
  531.     begin
  532.         oe := ValidateConnection(connection);
  533.         if oe = nOErr then
  534.             if writeCount > 0 then begin
  535.                 wds.buffer := writePtr;
  536.                 wds.size := writeCount;
  537.                 wds.term := 0;
  538.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  539.                 cb.send.wds := @wds;
  540.                 oe := PBControlSync(cb);
  541.             end
  542.             else if writeCount < 0 then
  543.                 oe := InvalidLength;
  544.         TCPSend := oe;
  545.     end;
  546.  
  547. {$PUSH}
  548. {$D-}
  549. procedure TCPSendComplete (cbp: MyControlBlockPtr);
  550.     var
  551.         oe: OSErr;
  552.     begin
  553.         AddBlock(cbp^.tcp.send.wds);
  554.         with cbp^.tcpc^ do begin
  555.             asendcompletes := asendcompletes + 1;
  556.             if (asendcompletes = asends) and closedone then begin
  557.                 asendcompletes := asendcompletes - 1; { Avoid race condition with TCPClose }
  558.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  559. { GetCB won't NewPtr because the completion has just released a block }
  560.                 if oe = noErr then
  561.                     oe := PBControlAsync(cbp);
  562.             end;
  563.         end;
  564.     end;
  565. {$POP}
  566.  
  567. function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  568.     type
  569.         myblock = record
  570.                 wds: wdsType;
  571.                 data: array[0..100] of byte;
  572.             end;
  573.         myblockptr = ^myblock;
  574.     var
  575.         oe: OSErr;
  576.         cbp: MyControlBlockPtr;
  577.         p: myblockptr;
  578.     begin
  579.         oe := ValidateConnection(connection);
  580.         if oe = nOErr then
  581.             if writeCount > 0 then begin
  582.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  583.                 if p = nil then
  584.                     oe := memFullErr
  585.                 else begin
  586.                     p^.wds.buffer := @p^.data;
  587.                     p^.wds.size := writeCount;
  588.                     p^.wds.term := 0;
  589.                     with p^.wds do
  590.                         BlockMove(writePtr, buffer, size);
  591.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  592.                     cbp^.tcp.send.wds := POINTER(p);
  593.                     with connection^ do
  594.                         asends := asends + 1;
  595.                     oe := PBControlAsync(cbp);
  596.                     if oe <> noErr then
  597.                         DisposPtr(ptr(p));
  598.                 end;
  599.             end
  600.             else if writeCount < 0 then
  601.                 oe := InvalidLength;
  602.         TCPSendAsync := oe;
  603.     end;
  604.  
  605. function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  606.     var
  607.         oe: OSErr;
  608.         cbp: MyControlBlockPtr;
  609.     begin
  610.         oe := ValidateConnection(connection);
  611.         if oe = noErr then
  612.             with connection^ do begin
  613.                 closeuserptr := userptr;
  614.                 if userptr <> nil then
  615.                     userptr^ := inProgress;
  616.                 closedone := true;
  617.                 if asends = asendcompletes then begin
  618.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  619.                     if oe = noErr then begin
  620.                         oe := PBControlAsync(cbp);
  621.                     end;
  622.                 end;
  623.             end;
  624.         SetUserPtr(userptr, oe);
  625.         TCPClose := oe;
  626.     end;
  627.  
  628. function TCPAbort (connection: TCPConnectionPtr): OSErr;
  629.     var
  630.         oe: OSErr;
  631.         cb: TCPControlBlock;
  632.     begin
  633.         oe := ValidateConnection(connection);
  634.         if oe = noErr then begin
  635.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  636.             oe := PBControlSync(cb);
  637.         end;
  638.         TCPAbort := oe;
  639.     end;
  640.  
  641. { Release the TCP stream, including the buffer.}
  642. function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  643.     var
  644.         oe: OSErr;
  645.         cb: TCPControlBlock;
  646.     begin
  647.         oe := ValidateConnection(connection);
  648.         if oe = noErr then begin
  649.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  650.             oe := PBControlSync(cb);
  651.             DestroyConnection(connection);
  652.         end;
  653.         TCPRelease := oe;
  654.     end;
  655.  
  656. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  657. procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  658.     var
  659.         cb: TCPControlBlock;
  660.         oe: OSErr;
  661.     begin
  662.         oe := ValidateConnection(connection);
  663.         localhost := 0;
  664.         localport := 0;
  665.         remotehost := 0;
  666.         remoteport := 0;
  667.         available := 0;
  668.         if oe <> noErr then begin
  669.             state := 99; { Error -> Closed }
  670.         end
  671.         else begin
  672.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  673.             if PBControlSync(cb) <> noErr then begin
  674.                 state := 99; { Closed }
  675.             end
  676.             else begin
  677.                 state := cb.status.connectionState;
  678.                 localhost := cb.status.localhost;
  679.                 localport := cb.status.localport;
  680.                 remotehost := cb.status.remotehost;
  681.                 remoteport := cb.status.remoteport;
  682.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  683.             end;
  684.         end;
  685.     end;
  686.  
  687. { Return the state of the TCP connection.}
  688. function TCPState (connection: TCPConnectionPtr): TCPStateType;
  689.     var
  690.         state: integer;
  691.         localhost: longInt;
  692.         localport: integer;
  693.         remotehost: longInt;
  694.         remoteport: integer;
  695.         available: longInt;
  696.     begin
  697.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  698.         case state of
  699.             0: 
  700.                 TCPState := T_Closed;
  701.             2: 
  702.                 TCPState := T_Listening;
  703.             4, 6: 
  704.                 TCPState := T_Opening;
  705.             8: 
  706.                 TCPState := T_Established;
  707.             10, 12, 16, 18, 20: 
  708.                 TCPState := T_Closing;
  709.             14: 
  710.                 TCPState := T_PleaseClose;
  711.             98: 
  712.                 TCPState := T_WaitingForOpen;
  713.             99: 
  714.                 TCPState := T_Closed;
  715.             otherwise
  716.                 TCPState := T_Unknown;
  717.         end;
  718.     end;
  719.  
  720. {    Return the number of characters available for reading from the TCP connection.}
  721. function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  722.     var
  723.         state: integer;
  724.         localhost: longInt;
  725.         localport: integer;
  726.         remotehost: longInt;
  727.         remoteport: integer;
  728.         available: longInt;
  729.     begin
  730.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  731.         TCPCharsAvailable := available;
  732.     end;
  733.  
  734. function TCPLocalPort (connection: TCPConnectionPtr): integer;
  735.     var
  736.         state: integer;
  737.         localhost: longInt;
  738.         localport: integer;
  739.         remotehost: longInt;
  740.         remoteport: integer;
  741.         available: longInt;
  742.     begin
  743.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  744.         TCPLocalPort := localport;
  745.     end;
  746.  
  747. function TCPFlush (connection: TCPConnectionptr): OSErr;
  748.     var
  749.         buffer: array[0..255] of signedByte;
  750.         f: longInt;
  751.         oe: OSErr;
  752.     begin
  753.         f := TCPCharsAvailable(connection);
  754.         oe := noErr;
  755.         while (f > 0) and (oe = noErr) do begin
  756.             if f > 256 then
  757.                 f := 256;
  758.             oe := TCPReceiveChars(connection, @buffer, f);
  759.             if oe = noErr then
  760.                 f := TCPCharsAvailable(connection);
  761.         end;
  762.         TCPFlush := oe;
  763.     end;
  764.  
  765. end.